package org.yoki.edu.bg.radius.filter.app.receiver;

import org.yoki.edu.bg.radius.core.client.domain.Client;
import org.yoki.edu.bg.radius.core.server.domain.Server;
import org.yoki.edu.bg.radius.filter.app.receiver.packet.RadiusPacket;
import org.yoki.edu.bg.radius.filter.app.transit.SynPacketTransit;
import com.yunzhitx.cloud.common.protocol.radius.msg.packet.RadiusMsg;
import com.yunzhitx.cloud.common.protocol.radius.msg.packet.check.resp.AccessRejectMsg;
import com.yunzhitx.cloud.common.protocol.radius.msg.parser.RadiusPacketParser;
import tk.mybatis.mapper.util.StringUtil;

import java.io.IOException;
import java.net.*;
import java.util.Objects;

/**
 * @author Sky$
 * @Description: TODO
 * @date 2018/1/3$ 14:08$
 */
public abstract class ForwardRadiusPacketReceiver extends RadiusPacketReceiver {

    public ForwardRadiusPacketReceiver() {
    }

    public ForwardRadiusPacketReceiver(SynPacketTransit observable) {
        super(observable);
    }

    public abstract boolean getIsAcct();

    public abstract int getRadiusPort();


    @Override
    public RadiusPacket beforeNotify(DatagramPacket packet) {
        RadiusPacket radiusPacket = new RadiusPacket(getIsAcct(), false, packet);
        return radiusPacket;
    }

    @Override
    public RadiusPacket afterNotify(RadiusPacket radiusPacket) {
        if (null == radiusPacket) {
            throw new NullPointerException();
        }
        //获取UDP数据包
        DatagramPacket packet = radiusPacket.getPacket();
        String ip = packet.getAddress().getHostAddress();
        int clientPort = packet.getPort();
        InetAddress clientAddr = packet.getAddress();
        System.out.println("Ip :  " + ip);
        System.out.println("------------------------------ 校验IP合法性 start ------------------------------");
        Client client = Client.selectDetailByIpAddr(ip);
        if (null == client && !client.getIsWork()) {
            System.out.println("Ip不合法，拒绝接入请求!!!");
            return null;
        }

        Server server = Server.getServerRepository().selectByPrimaryKey(client.getServerId());
        if (null == server || StringUtil.isEmpty(server.getIpAddr())) {
            System.out.println("未配置server端，拒绝接入请求!!!");
            return null;
        }
        String clientSecret = client.getSecret();
        String serverSecret = server.getSecret();
        String serverIp = server.getIpAddr();
        byte[] rawBytes = new byte[packet.getLength()];
        System.arraycopy(packet.getData(), 0, rawBytes, 0, rawBytes.length);
        RadiusPacket returnRadiusPacket = null;
        if (Objects.equals(clientSecret, serverSecret)) {
            returnRadiusPacket = forwarding(rawBytes, clientAddr, clientPort, serverIp);
        } else {
            returnRadiusPacket = encryptedForwarding(rawBytes, clientAddr, clientPort, clientSecret, serverIp, serverSecret);
        }
        //最后返回的数据包
        return returnRadiusPacket;
    }

    /**
     * 重新加密转发
     *
     * @param clientBytes
     * @return
     */
    private RadiusPacket encryptedForwarding(byte[] clientBytes, InetAddress clientIp,
                                             Integer clientPort, String clientSecret,
                                             String serverIp, String serverSecret) {
        //最后返回的数据包
        RadiusPacket returnRadiusPacket = null;
        try {
            byte[] rawBytes = clientBytes;
            //去除UDP数据包中只包含有效数据的一部分
            RadiusMsg clientMsg = RadiusPacketParser.parseReqPacket(rawBytes, clientSecret);
//            System.out.println("Client msg secret : " + clientSecret + " , msg : " + clientMsg);

            DatagramSocket socket = new DatagramSocket();
            //配置接收超时时间，单位：毫秒
            socket.setSoTimeout(10000);
            //配置发送给radius的数据包以及radius的地址和端口
            byte[] toRadiusBytes = clientMsg.getBytes(serverSecret);
            RadiusMsg reEncryptClientMsg = RadiusPacketParser.parseReqPacket(toRadiusBytes, serverSecret);
//            System.out.println("ReEncrypt Client msg : " + serverSecret + " , msg : " + reEncryptClientMsg);
            DatagramPacket toRadiusPacket = new DatagramPacket(toRadiusBytes, toRadiusBytes.length, InetAddress.getByName(serverIp), getRadiusPort());
            byte[] fromRadiusBuffer = new byte[0XFF00];
            DatagramPacket fromRadiusPacket = new DatagramPacket(fromRadiusBuffer, fromRadiusBuffer.length);
            //将客户端的数据发送给radius
            socket.send(toRadiusPacket);
            //接收radius的返回数据
            socket.receive(fromRadiusPacket);


            //将radius的返回数据发送回给客户端
            int sourcePort = clientPort;
            InetAddress sourceAddr = clientIp;
            byte[] fromRadiusBytes = new byte[fromRadiusPacket.getLength()];
            System.arraycopy(fromRadiusPacket.getData(), 0, fromRadiusBytes, 0, fromRadiusBytes.length);
            RadiusMsg serverMsg = RadiusPacketParser.parseRespPacket(fromRadiusBytes, serverSecret, reEncryptClientMsg);
//            System.out.println("Server msg secret : " + serverSecret + " , msg : " + serverMsg);
            byte[] returnBytes = serverMsg.getBytes(clientSecret);
            RadiusMsg reEncryptServerMsg = RadiusPacketParser.parseRespPacket(returnBytes, clientSecret, clientMsg);
//            System.out.println("ReEncrypt Server msg secret : " + clientSecret + " , msg : " + reEncryptServerMsg);
            DatagramPacket returnPacket = new DatagramPacket(returnBytes, returnBytes.length, sourceAddr, sourcePort);
            socket.send(returnPacket);
            //生成返回的RadiusPacket
            returnRadiusPacket = new RadiusPacket(getIsAcct(), true, returnPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return returnRadiusPacket;
    }

    private RadiusPacket forwarding(byte[] clientBytes, InetAddress clientIp, Integer clientPort, String serverIp) {
        //最后返回的数据包
        RadiusPacket returnRadiusPacket = null;
        try {
            byte[] rawBytes = clientBytes;

            DatagramSocket socket = new DatagramSocket();
            //配置接收超时时间，单位：毫秒
            socket.setSoTimeout(10000);
            //配置发送给radius的数据包以及radius的地址和端口
            byte[] toRadiusBytes = rawBytes;
            DatagramPacket toRadiusPacket = new DatagramPacket(toRadiusBytes, toRadiusBytes.length, InetAddress.getByName(serverIp), getRadiusPort());
            byte[] fromRadiusBuffer = new byte[0XFF00];
            DatagramPacket fromRadiusPacket = new DatagramPacket(fromRadiusBuffer, fromRadiusBuffer.length);
            //将客户端的数据发送给radius
            socket.send(toRadiusPacket);
            //接收radius的返回数据
            socket.receive(fromRadiusPacket);


            //将radius的返回数据发送回给客户端
            int sourcePort = clientPort;
            InetAddress sourceAddr = clientIp;
            byte[] fromRadiusBytes = new byte[fromRadiusPacket.getLength()];
            System.arraycopy(fromRadiusPacket.getData(), 0, fromRadiusBytes, 0, fromRadiusBytes.length);
            byte[] returnBytes = fromRadiusBytes;
            DatagramPacket returnPacket = new DatagramPacket(returnBytes, returnBytes.length, sourceAddr, sourcePort);
            socket.send(returnPacket);
            //生成返回的RadiusPacket
            returnRadiusPacket = new RadiusPacket(getIsAcct(), true, returnPacket);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return returnRadiusPacket;
    }

    @Override
    protected void exceptionHandle(DatagramPacket packet, Exception e) {
        System.out.println("Prepare to send rejection message to customer !!!");
        int sourcePort = packet.getPort();
        InetAddress sourceAddr = packet.getAddress();
        try {
            DatagramSocket socket = new DatagramSocket();

            String ip = packet.getAddress().getHostAddress();
            Client entity = Client.selectDetailByIpAddr(ip);
            if (null == entity) {
                return;
            }
            String secret = entity.getSecret();
            byte[] rawBytes = new byte[packet.getLength()];
            //去除UDP数据包中只包含有效数据的一部分
            System.arraycopy(packet.getData(), 0, rawBytes, 0, rawBytes.length);
            RadiusMsg clientMsg = RadiusPacketParser.parseReqPacket(rawBytes, secret);

            AccessRejectMsg rejectMsg = new AccessRejectMsg(secret, clientMsg);
            byte[] returnBytes = rejectMsg.getBytes();
            DatagramPacket returnPacket = new DatagramPacket(returnBytes, returnBytes.length, sourceAddr, sourcePort);
            socket.send(returnPacket);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

}
